using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text.RegularExpressions;

namespace SurveyHost.Core.Controls
{
    /// <summary>
    /// Base Survey Question control.  Used as the base for all survey questions
    /// </summary>
    /// <remarks>
    /// Extenders should override CreateChildControls to create all the child controls that constitute the control's web interface.
    /// </remarks>
    [DefaultProperty("QuestionText")]
    [ToolboxData("<{0}:BaseSurveyQuestion runat=server></{0}:BaseSurveyQuestion>")]
    public abstract class BaseSurveyQuestion : WebControl, ISurveyQuestion, INamingContainer
    {

        /// <summary>
        /// Help text that can be displayed to users to instruct them how to generate a configuration string.
        /// </summary>
        /// <remarks>Eventually will be replaced by a custom web control used to generate the configuration string.</remarks>
        public abstract string ConfigurationHelpText
        {
            get;
        }
        
        #region  Properties and variables (7)
        private const string DEFAULTFORMAT = "{0}: {1}";
        private const string APPEARANCE = "Appearance";
        private const string BEHAVIOR = "Behavior";

        /// <summary>
        /// The response from the user
        /// </summary>
        /// <remarks>Note to extenders:  AnswerText = null indicates the user refused to answer.</remarks>
        [Bindable(true), Category(BEHAVIOR), Browsable(true), Description("The answer given")]
        public virtual string AnswerText
        {
            get
            {
                return GetViewstateItem<string>(ANSWER,
                    delegate()
                    {
                        return string.Empty;
                    });
            }

            set
            {
                SetViewstateItem(ANSWER, value);
            }
        }
        private const string ANSWER = "Answer";

        /// <summary>
        /// The ID of the question
        /// </summary>
        [Bindable(true), Category(BEHAVIOR), Browsable(true), Description("The ID of the question")]
        public virtual Guid QuestionId
        {
            get
            {
                return GetViewstateItem<Guid>(QUESTIONID,
                    delegate()
                    {
                        return new Guid();
                    });
            }

            set
            {
                SetViewstateItem(QUESTIONID, value);
            }
        }
        private const string QUESTIONID = "QuestionId";

        /// <summary>
        /// The name of the measure this question belongs to
        /// </summary>
        /// <value></value>
        /// <remarks>Simple surveys have one measure; complex surveys have multiple measures.
        /// Each question is associated with its measure in the database; this property is used to
        /// identify which measure the question is associated with.  Therefore the measure must be spelled
        /// exactly as it is recorded.  A failure to do so would result in a SqlException being thrown.</remarks>
        [Browsable(true), Bindable(true), Category(BEHAVIOR),
        Description("The name of the measure this question belongs to")]
        public string MeasureName
        {
            get
            {
                return GetViewstateItem<string>(MEASURENAME,
                    delegate()
                    {
                        return string.Empty;
                    });
            }
            set
            {
                SetViewstateItem(MEASURENAME, value);
            }
        }
        private const string MEASURENAME = "MeasureName";

        /// <summary>
        /// The question number.
        /// </summary>
        /// <value></value>
        /// <remarks>Numbering should restart with each new measure</remarks>
        [Bindable(true), Category(BEHAVIOR), Browsable(true), Description("The question number")]
        public int QuestionIndex
        {
            get
            {
                return GetViewstateItem<int>(QUESTIONINDEX,
                    delegate()
                    {
                        return -1;
                    });
            }
            set
            {
                SetViewstateItem(QUESTIONINDEX, value);
            }
        }
        private const string QUESTIONINDEX = "QuestionIndex";

        /// <summary>
        /// The question to ask
        /// </summary>
        /// <value></value>
        [Bindable(true), Category(BEHAVIOR), DefaultValue(""),
        Localizable(true), Browsable(true), Description("The question text")]
        public string QuestionText
        {
            get
            {
                return GetViewstateItem<string>(QUESTIONTEXT,
                    delegate()
                    {
                        return string.Empty;
                    });
            }
            set
            {
                SetViewstateItem(QUESTIONTEXT, value);
            }
        }
        private const string QUESTIONTEXT = "QuestionText";

        /// <summary>
        /// The format string of the question line.  Requires two placeholders:  {0} for question number, {1} for the question text.
        /// The default is: 0}.&amp;nbsp;{1}&lt;br />
        /// </summary>
        [Bindable(true), Category(APPEARANCE), DefaultValue(DEFAULTFORMAT),
       Localizable(false), Description("The format string used to combine the QuestionIndex and QuestionText")]
        public string QuestionLineFormat
        {
            get
            {
                return GetViewstateItem<string>(QUESTIONLINEFORMAT,
                    delegate()
                    {
                        return string.Empty;
                    });
            }
            set
            {
                SetViewstateItem(QUESTIONLINEFORMAT, value);
            }
        }
        private const string QUESTIONLINEFORMAT = "QuestionLineFormat";

        /// <summary>
        /// The configuration string for this control.
        /// </summary>
        /// <remarks>
        /// The string consists of 1 to N text value pairs.  Each pair is separated by semicolons.
        /// Each pair is separated by a comma.  The text is the text of a single radio button option
        /// among the total number of options.  The value is the value returned if this item is selected.
        /// An example would be:  Yes;1|No;2|I don't know;3
        /// Pipes and semicolons are not allowed within either the question text or the value.
        /// </remarks>
        [Bindable(true), Category(BEHAVIOR), DefaultValue(""),
       Localizable(false), Description("The configuration string for the control")]
        public string ConfigurationString
        {
            get
            {
                return GetViewstateItem<string>(CONFIGURATIONSTRING,
                    delegate()
                    {
                        return string.Empty;
                    });
            }
            set
            {
                SetViewstateItem(CONFIGURATIONSTRING, value);
            }
        }
        private const string CONFIGURATIONSTRING = "ConfigurationString";
        #endregion

        /// <summary>
        /// Raises the <see cref="E:System.Web.UI.Control.Load"></see> event.
        /// </summary>
        /// <param name="e">The <see cref="T:System.EventArgs"></see> object that contains the event data.</param>
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            EnsureChildControls();
        }


        /// <summary>
        /// Called by the base class at the time it is appropriate to parse the configuration string,
        /// The configuration can be stored or used to create controls.
        /// </summary>
        protected virtual void ParseConfigurationString()
        {
        }

        /// <summary>
        /// Fired when an answer has been given
        /// </summary>
        public event EventHandler<AnswerEventArgs> AnswerGiven;

        /// <summary>
        /// Raises the <see cref="E:AnswerGiven"/> event.
        /// </summary>
        /// <param name="e">The <see cref="SurveyHost.Core.Controls.AnswerEventArgs"/> instance containing the event data.</param>
        public void OnAnswerGiven(AnswerEventArgs e)
        {
            EventHandler<AnswerEventArgs> temp = AnswerGiven;
            if (temp != null)
            {
                temp(this, e);
            }
        }

        /// <summary>
        /// A delegate used by <seealso cref="GetViewstateItems"/> to return the default value for an item
        /// </summary>
        /// <typeparam name="T">The type of item to return</typeparam>
        /// <returns>The default value of an item that is stored in the viewstate</returns>
        protected delegate T CreateDefault<T>();


        /// <summary>
        /// Gets an item from the viewstate
        /// </summary>
        /// <typeparam name="T">The type of the item to return</typeparam>
        /// <param name="key">The key string for the item in the viewstate.</param>
        /// <param name="generator">The generator delegate .</param>
        /// <returns>The item if in the viewstate, the return value of <paramref name="generator"/> if not found.</returns>
        protected T GetViewstateItem<T>(string key, CreateDefault<T> generator)
        {
            object value = ViewState[key];
            if (value == null || value.GetType() != typeof(T))
                return generator();
            return (T)value;
        }

        /// <summary>
        /// Sets an item into the viewstate.
        /// </summary>
        /// <param name="key">The key of the item to store</param>
        /// <param name="value">The value of the item to store</param>
        protected void SetViewstateItem(string key, object value)
        {
            ViewState[key] = value;
        }
    }

}
